#include "Precomp.h"
#include "MapState.h"
#include "IEntity.h"
#include "PluggableFactory.h"
#include "Game.h"
#include "mapformat.pb.h"

#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/construct.hpp>
#include <algorithm>

using namespace boost;
using namespace std;

namespace SGF {

MapState::MapState()
:deleteOnLeave(true),sceneManager(0)
{
}

MapState::~MapState()
{
	std::for_each(
		entities.begin(),
		entities.end(),
		lambda::bind(lambda::delete_ptr(),lambda::_1));//delete all entities
	if(sceneManager)
		game->getOgreRoot()->destroySceneManager(sceneManager);		
}

IEntity* MapState::createEntity(const char* className,IEntity* parent)
{
	IEntity* entity=(IEntity*)(PluggableFactory::getInstance().create(className));
	if(!entity)
		return NULL;
	entity->map=this;
	entity->onCreate();//call the "polymorphic constructor"
	if(parent)//if the entity needs a parent
	{//add it to the parent
		entity->parent=parent;
		parent->childEntityList.push_back(entity);
		parent->onChildAttached(entity);
	}
	else//add it to this map
		addEntity(entity);
	return entity;
}

void MapState::destroyEntity(IEntity* entity)
{
	IEntity* parent=entity->parent;
	setActive(entity,false);
	if(parent)
	{//if it has a parent
		parent->childEntityList.remove(entity);
		parent->onChildDetached(entity);
		delete entity;
	}
	else
	{//if it is in the map
		removeEntity(entity);
		delete entity;
	}
}

void MapState::addEntity(IEntity* entity)
{
	entities.push_back(entity);
}

void MapState::removeEntity(IEntity* entity)
{
	entities.remove(entity);
}

void MapState::iterateEntites(IEntityIterator* iterator)
{
	foreach(IEntity* entity,entities)
	{
		bool next=iterator->iterate(entity);
		if(!next)
			return;
	}
}

void MapState::setActive(IEntity* entity, bool active)
{
	if(active&&!(entity->active))//turn on
	{
		entity->updateConnection=updateSignal.connect(bind(&IEntity::onUpdate,entity,_1),*entity);
	}
	else if(!active&&(entity->active))//turn off
	{
		entity->updateConnection.disconnect();
	}
	entity->active=active;
}

bool MapState::isActive(IEntity* entity) const
{
	return entity->active;
}

void MapState::saveEntity(IEntity* entity,mapformat::Entity* entityData) const
{
	PluggableFactory& factory=PluggableFactory::getInstance();
	const char* typeName=factory.getProductName(entity);//retrive the typename from the factory
	entityData->set_type(typeName);//set type name
	entity->onSave(SaveData(entityData));
	foreach(IEntity* child,(entity->childEntityList))
	{
		mapformat::Entity* childData=entityData->add_children();
		saveEntity(child,childData);
	}
}

void MapState::loadEntity(mapformat::Entity* entityData,IEntity* parent)
{
	IEntity* entity=createEntity(entityData->type().c_str(),parent);
	if(!entity)
	{
		return;
	}
	entity->onLoad(entityData);
	::google::protobuf::RepeatedPtrField< ::SGF::mapformat::Entity >* children=entityData->mutable_children();
	foreach(mapformat::Entity& child,*children)
	{
		loadEntity(&child,entity);
	}
}

void MapState::onCreate()
{
}

void MapState::onEnter()
{
	std::for_each(entities.begin(),entities.end(),lambda::bind(&IEntity::onStateBegin,lambda::_1));
}

void MapState::onLeave()
{
	std::for_each(entities.begin(),entities.end(),lambda::bind(&IEntity::onStateEnd,lambda::_1));
	if(deleteOnLeave)
		delete this;
}

void MapState::update(Ogre::Real timeSinceLastFrame)
{
	updateSignal(timeSinceLastFrame);	
}

Ogre::SceneManager* MapState::getSceneManager() const
{
	return sceneManager;
}

}